Service 学习要点

1.Service概述

Service的主要作用是让系统在后台在后台做一些不与用户交互的操作(例如耗时操作:下载网络资源,长期运行的操作:播放音乐)
Service与Thread的区别:(1)Service不是在一个独立的进程中,它与我们的应用程序在同一进程(process)中 (2)Service也不是一个线程,相反,它是运行在主线程的(即UI线程),因此若我们要在Service中进行耗时操作时,需要开启一个子线程,在其中进行耗时操作,否则很容易出现ANR错误(Application Not Responding 程序无响应)

2.Service用法

ServiceTest.java如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ServiceTest extends Service {
private MyLocalBinder myLocalBinder=new MyLocalBinder();
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG","onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("TAG","onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("TAG","onBind");
return myLocalBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("TAG","onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("TAG","onDestroy");
}
//对外提供的访问方法
public void downLoad(){
Log.e("downLoad","正在下载...");
}
public void undownLoad(){
Log.e("downLoad","取消下载...");
}
class MyLocalBinder extends Binder{
public ServiceTest getServiceTestInstance(){
return ServiceTest.this;
}
//...这里也可以继续写方法对外提供
}
}

与Activity相似,使用Service也需要通过intent,不能忘记的是在使用Service前,需要在AndroidManifest.xml中进行声明

启动方式一:startService()

通过打印Service的生命周期,我们发现第一次启动Service的时候,会执行onCreate()和onStartCommand(),
再次启动时,只会执行onStartCommand(),也就是说onCreate()只会在第一次启动的时候进行初始化
点击“stopService”后,Service被销毁,进入onDestroy()方法。不管我们启动了多少次Service,只要我们在外部调用一次Context.stopService()或者在Service内部调用stopSelf(),Service就会被销毁

上面这种启动方式的缺点:启动完Service后,这个Service就在后台运行了,同时也与启动它的Activity失去了联系,因为不能通过ServiceTest service = new ServiceTest()的方式启动Service,因而我们的Activity中不能获取到ServiceTest的实例。
为了解决与启动Service的组件的通信能力,还有一个解决方案就是通过广播的形式。我们在Activity中发出一些想用操作广播,在Service中注册该广播,Service接收到该广播信息后,完成相应的功能。但是频繁发送广播比较消耗性能,同时,由于广播接受者中的onReceive()中,不能执行长时间的工作,时间超过后,可能就直接跳出了方法。因此,这种方案不是首选。

启动方式二:bindService() Bound机制

通过bindService()方式第一次启动后,会执行onCreate()和onBind()方法,当我们点击“unBindService“时,走的是onUnbind()和onDestroy()方法。如果有另一个组件对同一个Service进行bindService()操作(也就是在bindService()中传入不同的ServiceConnection,此时只会进入onBind()方法,即onCreate()只会在第一次启动的时候进行初始化

总结:可以看到,不管是通过哪种方式启动Service,同一个Service在整个应用程序中只有一个实例存在。区别:(1)两种方式所走的生命周期是不一样的(2)何时被销毁:当我们通过startService()启动时,不管我们启动了多少次Service,只要我们在外部调用一次Context.stopService()或者在Service内部调用stopSelf(),Service就会被销毁;而当我们通过bindService()启动时,前面我们多次启动service后,当所有客户端发出unBindService(),这个Service将被系统销毁。(3)当Service即被startService()启动也被bindService()启动时,这种情况下,Service必须在既没有任何activity关联又停止的情况下,Service才会被销毁。

3.IntentService

我们在第一部分谈到,有时需要在service中进行耗时操作,此时就需要开启一个子线程,而对于这种需求,Android提供了IntentService给用户,intentservice内部已经帮我们开启了线程,我们只需要实现它的onHandleIntent方法,在里面实现我们的功能即可,注:intentservice不能处理多个线程的请求,但是可以处理多个service的请求(此处求解?)

IntentService提供的功能:(1)所有请求处理完成后自动停止服务(2)提供了默认onBind()的实现,直接返回null,意味着我们只能通过startService()的方式启动IntentService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyIntentService extends IntentService {
public MyIntentService(){
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.e("MyIntentService","Thread
is"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("OnDestroy","OnDestroy");
}
}

使用时需注意两点:首先要提供一个无参的构造方法,里面调用父类的有参构造方法,第二是实现onHandleIntent这个抽象方法。

4.前台Service

Service默认都是在后台默默运行的,用户基本察觉不到有Service在运行。此时,Service的优先级是比较低的,当系统资源不足的时候,易被销毁。因此,如果我们想让用户知道有Service在后台运行,如音乐播放器,或者想让Service一直保持运行状态,不容易被系统回收,此时,就可以考虑使用前台Service。前台Service是被认为是用户已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该进程。
用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onCreate() {
super.onCreate();
Log.e("TAG","onCreate");
Notification notification=new
Notification(R.mipmap.ic_launcher,"前台通知",System.currentTi meMillis());
Intent intent=new Intent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,
intent,0);
notification.setLatestEventInfo(this, "通知标题",
"前台Service内容", pendingIntent);
//设置到前台运行,第一个参数为通知notification的唯一ID
startForeground(1,notification);
}

(关于Notification在SDK23以后和SDK22之前用法不一样,上面是SDK22以前的,下面是SDK23以后的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Notification.Builder builder=new      
Notification.Builder(getApplication());
builder.setContentInfo("补充内容");
builder.setContentText("主内容区");
builder.setContentTitle("通知标题");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setTicker("新消息");
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
Intent intent = new Intent(getApplication(), MainActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(getApplication(), 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();

如果我们要移除这个前台Service,只需要调用stopService()即可